home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ohlutil.zip / LN.C < prev    next >
C/C++ Source or Header  |  1990-06-23  |  9KB  |  403 lines

  1. /* `ln' program to create links among files.
  2.    Copyright (C) 1986, 1989, 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Mike Parker and David MacKenzie. */
  19.  
  20. #include <stdio.h>
  21. #include <sys/types.h>
  22. #include <errno.h>
  23. #include "system.h"
  24. #include "getopt.h"
  25. #include "backupfile.h"
  26.  
  27. #ifdef STDC_HEADERS
  28. #include <stdlib.h>
  29. #else
  30. char *getenv ();
  31.  
  32. extern int errno;
  33. #endif
  34.  
  35. #ifndef _POSIX_SOURCE
  36. int link ();
  37. #endif
  38.  
  39. #ifdef S_IFLNK
  40. int symlink ();
  41. #endif
  42.  
  43. char *basename ();
  44. enum backup_type get_version ();
  45. int do_link ();
  46. int isdir ();
  47. int yesno ();
  48. void error ();
  49. void strip_trailing_slashes ();
  50. void usage ();
  51.  
  52. /* A pointer to the function used to make links.  This will point to either
  53.    `link' or `symlink'. */
  54. int (*linkfunc) ();
  55.  
  56. /* If nonzero, make symbolic links; otherwise, make hard links.  */
  57. int symbolic_link;
  58.  
  59. /* If nonzero, ask the user before removing existing files.  */
  60. int interactive;
  61.  
  62. /* If nonzero, remove existing files unconditionally.  */
  63. int force;
  64.  
  65. /* If nonzero, list each file as it is moved. */
  66. int verbose;
  67.  
  68. /* If nonzero, allow the superuser to make hard links to directories. */
  69. int hard_dir_link;
  70.  
  71. /* If nonzero, stdin is not a tty. */
  72. int stdin_not_tty;
  73.  
  74. /* The name by which the program was run, for error messages.  */
  75. char *program_name;
  76.  
  77. static struct option long_options[] = 
  78. {
  79.   {"backup", 0, NULL, 'b'},
  80.   {"directory", 0, &hard_dir_link, 1},
  81.   {"force", 0, &force, 1},
  82.   {"interactive", 0, &interactive, 1},
  83.   {"suffix", 1, NULL, 'S'},
  84.   {"symbolic", 0, &symbolic_link, 1},
  85.   {"verbose", 0, &verbose, 1},
  86.   {"version-control", 1, NULL, 'V'},
  87.   {NULL, 0, NULL, 0}
  88. };
  89.  
  90. void
  91. main (argc, argv)
  92.      int argc;
  93.      char **argv;
  94. {
  95.   int c;
  96.   int ind;
  97.   int errors;
  98.   int make_backups = 0;
  99.   char *version;
  100.  
  101.   version = getenv ("SIMPLE_BACKUP_SUFFIX");
  102.   if (version)
  103.     simple_backup_suffix = version;
  104.   version = getenv ("VERSION_CONTROL");
  105.   program_name = argv[0];
  106.   linkfunc = link;
  107.   symbolic_link = force = interactive = verbose = hard_dir_link = 0;
  108.   errors = 0;
  109.  
  110.   while ((c = getopt_long (argc, argv, "bdfisvS:V:", long_options, &ind))
  111.      != EOF)
  112.     {
  113.       if (c == 0 && long_options[ind].flag == NULL)
  114.     c = long_options[ind].val;
  115.       switch (c)
  116.     {
  117.     case 0:            /* Long-named option. */
  118.        break;
  119.     case 'b':
  120.       make_backups = 1;
  121.       break;
  122.     case 'd':
  123.       hard_dir_link = 1;
  124.       break;
  125.     case 'f':
  126.       force = 1;
  127.       break;
  128.     case 'i':
  129.       interactive = 1;
  130.       break;
  131.     case 's':
  132.       symbolic_link = 1;
  133.       break;
  134.     case 'v':
  135.       verbose = 1;
  136.       break;
  137.     case 'S':
  138.       simple_backup_suffix = optarg;
  139.       break;
  140.     case 'V':
  141.       version = optarg;
  142.       break;
  143.     default:
  144.       usage ();
  145.       break;
  146.     }
  147.     }
  148.   if (optind == argc)
  149.     usage ();
  150.  
  151.   if (make_backups)
  152.     backup_type = get_version (version);
  153.  
  154. #ifdef S_IFLNK
  155.   if (symbolic_link)
  156.     linkfunc = symlink;
  157. #endif
  158.  
  159.   if (interactive)
  160.     force = 0;
  161.   stdin_not_tty = !isatty (0);
  162.  
  163.   if (optind == argc - 1)
  164.     errors = do_link (argv[optind], ".");
  165.   else if (optind == argc - 2)
  166.     {
  167.       strip_trailing_slashes (argv[optind + 1]);
  168.       errors = do_link (argv[optind], argv[optind + 1]);
  169.     }
  170.   else
  171.     {
  172.       char *to;
  173.  
  174.       to = argv[argc - 1];
  175.       strip_trailing_slashes (to);
  176.       if (!isdir (to))
  177.     error (1, 0, "when making multiple links, last argument must be a directory");
  178.       for (; optind < argc - 1; ++optind)
  179.     errors += do_link (argv[optind], to);
  180.     }
  181.  
  182.   exit (errors != 0);
  183. }
  184.  
  185. /* Make a link `new' to existing file `old'.
  186.    If `new' is a directory, put the link to `old' in that directory.
  187.    Return 1 if there is an error, otherwise 0.  */
  188.  
  189. int
  190. do_link (old, new)
  191.      char *old;
  192.      char *new;
  193. {
  194.   struct stat old_stats, new_stats;
  195.   char *new_backup = NULL;
  196.  
  197.   strip_trailing_slashes (old);
  198.   if (lstat (old, &old_stats))
  199.     {
  200.       error (0, errno, "%s", old);
  201.       return 1;
  202.     }
  203.  
  204.   /* Since link follows symlinks, isdir uses stat instead of lstat. */
  205.   if (!symbolic_link && !hard_dir_link && isdir (old))
  206.     {
  207.       error (0, 0, "%s: hard link not allowed for directory", old);
  208.       return 1;
  209.     }
  210.   if (isdir (new))
  211.     {
  212.       /* Target is a directory; build the full filename. */
  213.       char *new_new;
  214.       char *old_base;
  215.  
  216.       old_base = basename (old);
  217.       new_new = (char *) alloca (strlen (old_base) + 1 + strlen (new) + 1);
  218.       sprintf (new_new, "%s/%s", new, old_base);
  219.       new = new_new;
  220.     }
  221.  
  222.   if (verbose)
  223.     printf ("  %s -> %s\n", old, new);
  224.  
  225.   if (lstat (new, &new_stats) == 0)
  226.     {
  227.       if ((new_stats.st_mode & S_IFMT) == S_IFDIR)
  228.     {
  229.       error (0, 0, "%s: cannot overwrite directory", new);
  230.       return 1;
  231.     }
  232.       if (interactive)
  233.     {
  234.       fprintf (stderr, "%s: replace `%s'? ", program_name, new);
  235.       if (!yesno ())
  236.         return 0;
  237.     }
  238.       else if (!force)
  239.     {
  240.       int may_overwrite;
  241.  
  242.       /* Treat the file as nonwritable if it lacks write permission bits,
  243.          even if we are root.  */
  244. #ifdef S_IFLNK
  245.       if ((new_stats.st_mode & S_IFMT) == S_IFLNK)
  246.         may_overwrite = 1;
  247.       else
  248. #endif
  249.         may_overwrite = eaccess_stat (&new_stats, W_OK) == 0
  250.           && (new_stats.st_mode & 0222);
  251.  
  252.       if (!may_overwrite)
  253.         {
  254.           if (stdin_not_tty)
  255.         {
  256.           error (0, 0, "%s: no write permission", new);
  257.           return 1;
  258.         }
  259.           fprintf (stderr, "%s: override mode %04o for `%s'? ",
  260.                program_name, new_stats.st_mode & 0777, new);
  261.           if (!yesno ())
  262.         return 0;
  263.         }
  264.     }
  265.  
  266.       if (backup_type != none)
  267.     {
  268.       new_backup = find_backup_file_name (new);
  269.       if (new_backup == NULL)
  270.         error (1, 0, "virtual memory exhausted");
  271.       if (rename (new, new_backup))
  272.         {
  273.           if (errno != ENOENT)
  274.         {
  275.           error (0, errno, "cannot backup `%s'", new);
  276.           free (new_backup);
  277.           return 1;
  278.         }
  279.           else
  280.         {
  281.           free (new_backup);
  282.           new_backup = NULL;
  283.         }
  284.         }
  285.     }
  286.       else if (unlink (new) && errno != ENOENT)
  287.     {
  288.       error (0, errno, "cannot remove old link to `%s'", new);
  289.       return 1;
  290.     }
  291.     }
  292.   else if (errno != ENOENT)
  293.     {
  294.       error (0, errno, "%s", new);
  295.       return 1;
  296.     }
  297.        
  298.   if ((*linkfunc) (old, new) == 0)
  299.     {
  300.       if (new_backup)
  301.     free (new_backup);
  302.       return 0;
  303.     }
  304.  
  305.   error (0, errno, "cannot %slink `%s' to `%s'",
  306. #ifdef S_IFLNK
  307.          linkfunc == symlink ? "symbolic " : "",
  308. #else
  309.          "",
  310. #endif
  311.          old, new);
  312.  
  313.   if (new_backup)
  314.     {
  315.       if (rename (new_backup, new))
  316.     error (0, errno, "cannot un-backup `%s'", new);
  317.       free (new_backup);
  318.     }
  319.   return 1;
  320. }
  321.  
  322. /* Return 1 if the user gives permission, 0 if not.  */
  323.  
  324. int
  325. yesno ()
  326. {
  327.   int c;
  328.   int rv;
  329.  
  330.   fflush (stderr);
  331.   c = getchar ();
  332.   rv = (c == 'y') || (c == 'Y');
  333.   while (c != EOF && c != '\n')
  334.     c = getchar ();
  335.  
  336.   return rv;
  337. }
  338.  
  339. /* Return 1 if `file' is a directory or a symlink to a directory;
  340.    otherwise 0. */
  341.  
  342. int
  343. isdir (file)
  344.      char *file;
  345. {
  346.   struct stat stats;
  347.  
  348.   return stat (file, &stats) == 0 && (stats.st_mode & S_IFMT) == S_IFDIR;
  349. }
  350.  
  351. /* Return `name' with any leading path stripped off.  */
  352.  
  353. char *
  354. basename (name)
  355.      char *name;
  356. {
  357.   char *base;
  358.  
  359.   base = rindex (name, '/');
  360.   return base ? base + 1 : name;
  361. }
  362.  
  363. /* Remove trailing slashes from PATH; they cause some system calls to fail. */
  364. void
  365. strip_trailing_slashes (path)
  366.      char *path;
  367. {
  368.   int last;
  369.  
  370.   last = strlen (path) - 1;
  371.   while (last > 0 && path[last] == '/')
  372.     path[last--] = '\0';
  373. }
  374.  
  375. void
  376. usage ()
  377. {
  378.   fprintf (stderr, "\
  379. Usage: %s [-bdfi%sv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  380.        [+version-control {numbered,existing,simple}] [+backup] [+directory]\n\
  381.        [+force] [+interactive] %s[+verbose] [+suffix backup-suffix]\n\
  382.        source [dest]\n\
  383. \n\
  384.        %s [-bdfi%sv] [-S backup-suffix] [-V {numbered,existing,simple}]\n\
  385.        [+version-control {numbered,existing,simple}] [+backup] [+directory]\n\
  386.        [+force] [+interactive] %s[+verbose] [+suffix backup-suffix]\n\
  387.        source... directory\n",
  388.        program_name,
  389. #ifdef S_IFLNK
  390.        "s", "[+symbolic] ",
  391. #else
  392.        "", "",
  393. #endif
  394.        program_name,
  395. #ifdef S_IFLNK
  396.        "s", "[+symbolic] "
  397. #else
  398.        "", ""
  399. #endif
  400.        );
  401.   exit (1);
  402. }
  403.